#include "stdafx.h"
#include "FileHandler.h"
#include <assert.h>
#include <algorithm>
#include <dwrite.h>
#include <string>

static const UINT kInvalidChunkOffset = UINT_MAX;
static const int kInvalidIndex = -1;


FileHandler::FileHandler()
{
	mFile = INVALID_HANDLE_VALUE;
	mAsyncData = nullptr;
	mAlignment = mSectorSize = 512;
	mIsReadingAsync = false;
	mProtectedOffsets[0] = mProtectedOffsets[1] = kInvalidChunkOffset;
	mIsSearching = false;
	mWasSearchInterrupted = false;

	mHeap = ::GetProcessHeap();
}


FileHandler::~FileHandler()
{
	CloseFile();
	if (mAsyncData)
	{
		::HeapFree(mHeap, 0, mAsyncData);
	}
}

bool FileHandler::OpenFile(LPCTSTR path)
{
	CloseFile();
	mFile = ::CreateFile(path, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
		(FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED | FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_NO_BUFFERING),
		nullptr);
	if (INVALID_HANDLE_VALUE == mFile)
	{
		if (nullptr != path && '\0' != path[0])
		{
			DWORD error = ::GetLastError();
			HLOCAL buffer = 0;
			DWORD n = ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
				nullptr, error, 0, (LPTSTR)&buffer, 0, nullptr);
			if (0 < n)
			{
				auto p = ::LocalLock(buffer);
				typedef std::basic_string<TCHAR> StringT;
				StringT w = LPCTSTR(p);
				w += L":\n(";
				w += path;
				w += L")";
				
				::MessageBox(0, w.c_str(), L"Error while opening file", MB_OK);
				::LocalUnlock(buffer);
			}

			if (buffer)
			{
				::LocalFree(buffer);
			}
		}
		return false;
	}
	LARGE_INTEGER size;
	if (!::GetFileSizeEx(mFile, &size))
	{
		size.QuadPart = 0;
	}
	mFileLength = size.QuadPart;
	mChunkMax = kInvalidChunkOffset;

	bool result = true;
	if (0 < mFileLength)
	{
		result = ResetAsyncParameters();
		if (result)
		{
			mChunkMax = UINT((mFileLength - 1) / mChunkSize);

			int length = (int)min(mFileLength, kCacheLimitForFile);
			int chunks = (length + mChunkSize - 1) / mChunkSize;
			size_t bufferSize = chunks * mChunkSize;
			if (bufferSize > mBuffer.size())
			{
				mBuffer.resize(bufferSize);
			}
			mChunkOffsets.assign(chunks, kInvalidChunkOffset);
			mRecentList.resize(2 * (chunks + 1));
			mRecentList[2 * chunks] = mRecentList[2 * chunks + 1] = chunks;
			mNumberOfNodes = 0;

			result = StartReadingChunk(0);
		}
	}

	return result;
}

LONGLONG FileHandler::GetFileLength() const
{
	return mFileLength;
}


void FileHandler::MakeReadInAdvance(LONGLONG inAddress, bool goingForward)
{
	UINT chunk = (UINT)(::floor(inAddress / mChunkSize));
	UINT nextChunk = GetNextChunkIndex(chunk, goingForward);

	// exclude the chunks that are already loaded
	for (auto x : mChunkOffsets)
	{
		if (kInvalidChunkOffset == x)
		{
			continue;
		}
		if (x == chunk)
		{
			chunk = kInvalidChunkOffset;
			if (kInvalidChunkOffset == nextChunk)
			{
				break;
			}
		}
		else if (x == nextChunk)
		{
			nextChunk = kInvalidChunkOffset;
			if (kInvalidChunkOffset == chunk)
			{
				break;
			}
		}
	}

	// start reading one of { chunk, nextChunk }
	if (kInvalidChunkOffset != chunk)
	{
		StartReadingChunk(chunk);
	}
	else if (kInvalidChunkOffset != nextChunk)
	{
		bool isNextChunkReady = false;
		if (mIsReadingAsync && mChunkBeingRead == nextChunk)
		{
			DWORD actualReadBytes;
			BOOL success = ::GetOverlappedResult(mFile, mOverlappedPtr,
				&actualReadBytes, false);
			if (success)
			{
				AcceptAsyncData();
				auto iterator = std::find(mChunkOffsets.cbegin(), mChunkOffsets.cend(), nextChunk);
				isNextChunkReady = (mChunkOffsets.cend() != iterator);
			}
			
		}
		if (!isNextChunkReady)
		{
			StartReadingChunk(nextChunk);
		}
	}
}

bool FileHandler::AreTwoAddressesWithinOneCachedChunk(
	LONGLONG inAddr1, LONGLONG inAddr2) const
{
	bool result = false;
	auto chunk = inAddr1 / mChunkSize;
	if ((inAddr2 / mChunkSize) == chunk)
	{
		auto iterator = std::find(mChunkOffsets.cbegin(), mChunkOffsets.cend(),
			UINT(chunk));
		result = (mChunkOffsets.cend() != iterator);
	}
	return result;
}

bool FileHandler::CopyBytes(LONGLONG address, BYTE* outBuffer, int& ioBuffLen)
{
	if (INVALID_HANDLE_VALUE == mFile)
	{
		return false;
	}
	int bytesRead = 0;
	UINT chunk = UINT((address + bytesRead) / mChunkSize);
	UINT chunksEnd = UINT((mFileLength + mChunkSize - 1) / mChunkSize);
	UINT offsetWithinChunk = UINT(address % mChunkSize);
	while (bytesRead < ioBuffLen && chunk < chunksEnd)
	{
		int index = ReadChunk(chunk);
		if (kInvalidIndex == index)
		{
			break;
		}
		const BYTE* p = &mBuffer[index * mChunkSize];
		UINT offsetEnd = mChunkSize;
		if (chunk == chunksEnd - 1)
		{
			UINT remainder = UINT(mFileLength % mChunkSize);
			if (0 < remainder)
			{
				offsetEnd = remainder;
			}
		}

		if (offsetWithinChunk >= offsetEnd)
		{
			break;
		}
		int remainingBufferBytes = ioBuffLen - bytesRead;
		int n = min(int(offsetEnd - offsetWithinChunk), remainingBufferBytes);
		::memcpy_s(outBuffer + bytesRead, remainingBufferBytes,
			p + offsetWithinChunk, n);
		bytesRead += n;
		offsetWithinChunk = (offsetWithinChunk + n) % mChunkSize;
		++chunk;
	}
	ioBuffLen = bytesRead;
	return (0 < bytesRead);
}

void FileHandler::ProtectCachingForAddressRange(LONGLONG inAddr, int inN)
{
	if (inAddr < 0 || inN < 0)
	{
		mProtectedOffsets[0] = mProtectedOffsets[1] = kInvalidChunkOffset;
		return;
	}

	if (inAddr >= mFileLength)
	{
		return;
	}
	UINT offset0 = UINT(inAddr / mChunkSize);
	UINT offset1 = UINT(min(inAddr + inN, mFileLength - 1) / mChunkSize);
	mProtectedOffsets[0] = offset0;
	mProtectedOffsets[1] = (offset1 == offset0 ? kInvalidChunkOffset : offset1);
}

bool FileHandler::SetWhatVector(const WhatCharactersVector& inVector)
{
	return mFindHelper.SetWhatVector(inVector);
}

bool FileHandler::FindData(BOOL inDownwards, LONGLONG& ioAddr, int& outRangeLength)
{
	mIsSearching = true;
	auto a = ioAddr;
	bool result = false;
	int numberOfSkipBeforeAdvanceReading = 3;
	if (inDownwards)
	{
		while (a < mFileLength)
		{
			auto pBuf = GetChunkBufferForAddress(a);
			if (--numberOfSkipBeforeAdvanceReading < 0 && a + mChunkSize < mFileLength)
			{
				UINT chunk = UINT(a / mChunkSize) + 1;
				auto iterator = std::find(mChunkOffsets.begin(), mChunkOffsets.end(), chunk);
				if (iterator == mChunkOffsets.end())
				{
					StartReadingChunk(chunk);
				}
				numberOfSkipBeforeAdvanceReading = 0;
			}
			int offset1 = int(a % mChunkSize);
			int offset2 = int(min(mChunkSize, mFileLength - a + offset1));
			assert(offset1 < offset2);
			mFindHelper.SetRange(pBuf + offset1, pBuf + offset2);

			int foundIndex1, foundIndex2;
			if (mFindHelper.SearchDown(foundIndex1, foundIndex2))
			{
				ioAddr = a + foundIndex1;
				outRangeLength = foundIndex2 - foundIndex1;
				result = true;
				break;
			}
			a += (mChunkSize - offset1);
		}
	}
	else
	{
		// a marks the end of word
		while (a >= 0)
		{
			auto pBuf = GetChunkBufferForAddress(a);
			if (--numberOfSkipBeforeAdvanceReading < 0 && a > mChunkSize)
			{
				UINT chunk = UINT(a / mChunkSize) - 1;
				auto iterator = std::find(mChunkOffsets.begin(), mChunkOffsets.end(), chunk);
				if (iterator == mChunkOffsets.end())
				{
					StartReadingChunk(chunk);
				}
				numberOfSkipBeforeAdvanceReading = 0;
			}

			int offset1 = int(a % mChunkSize);
			mFindHelper.SetRange(pBuf, pBuf + offset1 + 1);

			int foundIndex1, foundIndex2;
			if (mFindHelper.SearchUp(foundIndex1, foundIndex2))
			{
				ioAddr = a + foundIndex1 - offset1;
				outRangeLength = foundIndex2 - foundIndex1;
				result = true;
				break;
			}

			if (a < mChunkSize)
			{
				break;
			}
			a -= offset1 + 1;
		}
	}

	mIsSearching = false;
	return result;
}

void FileHandler::StartFind(BOOL downwards, LONGLONG addressFrom)
{
	mIsSearching = true;
	mWasSearchInterrupted = false;

	mSearchRangeLength = kInvalidChunkOffset;
	mIsDownwardSearch = downwards;
	mChunkBeingSearched = UINT(addressFrom / mChunkSize);
	if (downwards)
	{
		if (addressFrom < mFileLength)
		{
			auto pBuf = GetChunkBufferForAddress(addressFrom);
			int offset1 = int(addressFrom % mChunkSize);
			int offset2 = int(min(mChunkSize, mFileLength - addressFrom + offset1));
			assert(offset1 < offset2);
			mFindHelper.SetRange(pBuf + offset1, pBuf + offset2);

			int foundIndex1, foundIndex2;
			if (mFindHelper.SearchDown(foundIndex1, foundIndex2))
			{
				mSearchOffset = offset1 + foundIndex1;
				mSearchRangeLength = foundIndex2 - foundIndex1;
				mIsSearching = false;
			}
		}
		mIsSearching = mIsSearching && (mChunkBeingSearched < mChunkMax);
	}
	else
	{
		auto pBuf = GetChunkBufferForAddress(addressFrom);
		int offsetEnd = int(addressFrom % mChunkSize) + 1;
		mFindHelper.SetRange(pBuf, pBuf + offsetEnd);

		int foundIndex1, foundIndex2;
		if (mFindHelper.SearchUp(foundIndex1, foundIndex2))
		{
			mSearchOffset = foundIndex1;
			mSearchRangeLength = foundIndex2 - foundIndex1;
			mIsSearching = false;
		}
		else
		{
			mIsSearching = (0 < mChunkBeingSearched);
		}
	}
}
bool FileHandler::ContinueFind()
{
	auto iterator = std::find(mChunkOffsets.cbegin(), mChunkOffsets.cend(), mChunkBeingSearched);
	assert(mChunkOffsets.cend() != iterator);
	int index = iterator - mChunkOffsets.cbegin();
	const BYTE* data = &mBuffer[index * mChunkSize];

	if (mIsDownwardSearch)
	{
		int offsetLast = mChunkSize;
		if (mChunkBeingSearched == mChunkMax)
		{
			int remainder = int(mFileLength - LONGLONG(mChunkBeingSearched * mChunkSize));
			offsetLast = min(mChunkSize, remainder);
		}
		mFindHelper.SetRange(data, data + offsetLast);

		int foundIndex1, foundIndex2;
		if (mFindHelper.SearchDown(foundIndex1, foundIndex2))
		{
			mSearchOffset = foundIndex1;
			mSearchRangeLength = foundIndex2 - foundIndex1;
			mIsSearching = false;
		}
	}
	else
	{
		mFindHelper.SetRange(data, data + mChunkSize);
		int foundIndex1, foundIndex2;
		if (mFindHelper.SearchUp(foundIndex1, foundIndex2))
		{
			mSearchOffset = foundIndex1;
			mSearchRangeLength = foundIndex2 - foundIndex1;
			mIsSearching = false;
		}
	}
	return false;
}
bool FileHandler::LaunchAsyncReadingNextBlock()
{
	UINT chunk = mChunkBeingSearched;
	if (mIsDownwardSearch)
	{
		if (chunk + 1 <= mChunkMax)
		{
			StartReadingChunk(chunk + 1);
		}
	}
	else if (0 < chunk)
	{
		StartReadingChunk(chunk - 1);
	}
	return mIsReadingAsync;
}

void FileHandler::AcceptCurrentAsyncBlock()
{
	if (mIsReadingAsync)
	{
		DWORD actualBytesRead = 0;
		if (::GetOverlappedResult(mFile, mOverlappedPtr, &actualBytesRead, true))
		{
			AcceptAsyncData();
		}
		else
		{
			assert(false);
		}
	}
	// assert
	const auto iterator = std::find(mChunkOffsets.cbegin(),
		mChunkOffsets.cend(), mChunkBeingSearched);
	assert(mChunkOffsets.cend() != iterator);
}

HANDLE FileHandler::GetEventForAsyncReading() const
{
	HANDLE result = 0;
	if (IsReadingAsync())
	{
		result = mOverlappedPtr->hEvent;
	}
	return result;
}

bool FileHandler::IsSearchFinished() const
{
	return !mIsSearching;
}

bool FileHandler::WasSearchInterrupted() const
{
	return mWasSearchInterrupted;
}

void FileHandler::SetSearchFinished()
{
	mIsSearching = false;
	mWasSearchInterrupted = false;
}

bool FileHandler::IsAtLastSearchBlock() const
{
	return mIsDownwardSearch ? 
		(mChunkMax == mChunkBeingSearched) : (0 == mChunkBeingSearched);
}

void FileHandler::GotoNextSearchBlock()
{
	if (mIsDownwardSearch)
	{
		if (mChunkBeingSearched < mChunkMax)
		{
			++mChunkBeingSearched;
		}
	}
	else
	{
		if (0 < mChunkBeingSearched)
		{
			--mChunkBeingSearched;
		}
	}
}

bool FileHandler::IsReadingAsync() const
{
	return mIsReadingAsync;
}

bool FileHandler::IsDataCachedAtAddress(LONGLONG inAddress) const
{
	UINT chunk = UINT(inAddress / mChunkSize);
	auto iterator = std::find(mChunkOffsets.cbegin(), mChunkOffsets.cend(), chunk);
	return (mChunkOffsets.cend() != iterator);
}

bool FileHandler::IsDataCachedAroundAddress(LONGLONG inAddress,
	bool inNeighborChunkAbove) const
{
	bool result = false;
	UINT chunk = UINT(inAddress / mChunkSize);
	auto iterator = std::find(mChunkOffsets.cbegin(), mChunkOffsets.cend(), chunk);
	if (mChunkOffsets.cend() != iterator)
	{
		UINT nextChunk = GetNextChunkIndex(chunk, inNeighborChunkAbove);
		if (nextChunk != chunk)
		{
			iterator = std::find(mChunkOffsets.cbegin(), mChunkOffsets.cend(),
				nextChunk);
		}
		result = (mChunkOffsets.cend() != iterator);
	}
	return result;
}

bool FileHandler::StopReading()
{
	bool result = true;
	if (mIsReadingAsync)
	{
		mWasSearchInterrupted |= mIsSearching;
		::CancelIo(mFile);
		DWORD actualReadBytes = 0;
		BOOL getRes = ::GetOverlappedResult(mFile, mOverlappedPtr,
			&actualReadBytes, true);
		mIsReadingAsync = false;
	}
	return !mIsReadingAsync;
}


bool FileHandler::GetRangeOfFoundBytes(LONGLONG& outRangeStart, int& outRangeLength)
{
	if (0 < mSearchRangeLength)
	{
		outRangeLength = mSearchRangeLength;
		outRangeStart = LONGLONG(mChunkBeingSearched) * mChunkSize + mSearchOffset;
		return true;
	}
	return false;
}

// ---------- private ----------

void FileHandler::CloseFile()
{
	if (INVALID_HANDLE_VALUE != mFile)
	{
		StopReading();
		::CloseHandle(mFile);
	}
}

bool FileHandler::ResetAsyncParameters()
{
	FILE_STORAGE_INFO fsi;
	BOOL success = ::GetFileInformationByHandleEx(mFile, FileStorageInfo,
		&fsi, sizeof(fsi));
	if (!success)
	{
		return false;
	}
	
	int chunkUnit = (int)fsi.PhysicalBytesPerSectorForPerformance;
	mChunkSize = chunkUnit * (kDefaultChunkSize / chunkUnit);
	if (0 >= mChunkSize)
	{
		return false;
	}

	mSectorSize = (int)fsi.PhysicalBytesPerSectorForAtomicity;
	int highestBitMask = 0;
	for (int m = mSectorSize; m; m &= (m - 1))
	{
		highestBitMask = m;
	}
	mAlignment = (highestBitMask == mSectorSize ? highestBitMask : 2 * highestBitMask);

	int sufficientAsyncDataSize = mChunkSize + sizeof(OVERLAPPED) + mAlignment - 1;
	if (mAsyncData)
	{
		if (mAsyncDataSize < CalculatePointersInsideAsyncData())
		{
			mAsyncDataSize = sufficientAsyncDataSize;
			mAsyncData = ::HeapReAlloc(mHeap, 0, mAsyncData, mAsyncDataSize);
			CalculatePointersInsideAsyncData();
		}
	}
	else
	{
		mAsyncDataSize = sufficientAsyncDataSize;
		mAsyncData = ::HeapAlloc(mHeap, 0, mAsyncDataSize);
		CalculatePointersInsideAsyncData();
	}

	return (nullptr != mAsyncData);
}

int FileHandler::CalculatePointersInsideAsyncData()
{
	uintptr_t x0 = reinterpret_cast<uintptr_t>(mAsyncData);
	uintptr_t x = ((x0 - 1) | (mAlignment - 1)) + 1;
	mAsyncBuffer = reinterpret_cast<BYTE*>(x);
	mOverlappedPtr = reinterpret_cast<OVERLAPPED*>(x + mChunkSize);
	return (x + mChunkSize + sizeof(OVERLAPPED) - x0);
}

bool FileHandler::StartReadingChunk(UINT chunkNo)
{
	StopReading();
	assert(!mIsReadingAsync);

	ZeroMemory(mOverlappedPtr, sizeof(OVERLAPPED));
	LONGLONG chunkOffsetInFile = chunkNo * LONGLONG(mChunkSize);
	mOverlappedPtr->Offset = DWORD(chunkOffsetInFile);
	mOverlappedPtr->OffsetHigh = DWORD(chunkOffsetInFile >> 32);
	mOverlappedPtr->hEvent = ::CreateEvent(nullptr, false, false, nullptr);

	int bytesToRead = (int)min(mFileLength - chunkOffsetInFile, mChunkSize);
	// round up to the whole number of sector sizes
	bytesToRead = mSectorSize * ((bytesToRead + mSectorSize - 1) / mSectorSize);

	assert(bytesToRead <= mChunkSize);
	mChunkBeingRead = chunkNo;
	BOOL success = ::ReadFile(mFile, mAsyncBuffer, bytesToRead, nullptr,
		mOverlappedPtr);
	if (success)
	{
		AcceptAsyncData();
	}
	else
	{
		DWORD error = ::GetLastError();
		if (ERROR_IO_PENDING == error)
		{
			assert(0 != mOverlappedPtr->hEvent);
			mIsReadingAsync = true;
			success = true;
		}
	}
	return (false != success);
}

void FileHandler::AcceptAsyncData()
{
	OVERLAPPED* pMutableOverlapped = reinterpret_cast<OVERLAPPED*>(
		mAsyncBuffer + mChunkSize);
	if (HANDLE hEvent = pMutableOverlapped->hEvent)
	{
		::CloseHandle(hEvent);
		pMutableOverlapped->hEvent = 0;
	}
	mIsReadingAsync = false;

	const OVERLAPPED* pOverlapped = pMutableOverlapped;

	LONGLONG offsetLL = (LONGLONG(pOverlapped->OffsetHigh) << 32) |
		pOverlapped->Offset;
	UINT offset = UINT(offsetLL / mChunkSize);

	// find the proper place, the least useful chunk
	const int cacheSize = mChunkOffsets.size();
	int foundIndex = kInvalidIndex;
	if (mNumberOfNodes < cacheSize)
	{
		foundIndex = mNumberOfNodes;
		int prevI = mRecentList[2 * cacheSize];
		mRecentList[2 * prevI + 1] = foundIndex;
		mRecentList[2 * foundIndex] = prevI;
		mRecentList[2 * foundIndex + 1] = cacheSize;
		mRecentList[2 * cacheSize] = foundIndex;
		++mNumberOfNodes;
	}
	else
	{
		int indexOfFirst, indexOfLast, indexOfDistant;
		indexOfFirst = indexOfLast = indexOfDistant = kInvalidIndex;
		int mostRecentIndex = mRecentList[2 * cacheSize];
		UINT currentOffset = mChunkOffsets[mostRecentIndex];
		UINT distance = 0;
		assert(cacheSize > 2);

		int index = cacheSize;
		for (int i = cacheSize / 2; i--;)
		{
			index = mRecentList[2 * index + 1];
			UINT offset = mChunkOffsets[index];
			assert(offset <= mChunkMax);
			//
			if (offset == mProtectedOffsets[0] || offset == mProtectedOffsets[1])
			{
				continue;
			}

			if (0 == offset)
			{
				indexOfFirst = index;
				continue;
			}
			// compare with last... ?
			if (offset == mChunkMax)
			{
				indexOfLast = index;
				continue;
			}

			if (currentOffset > offset + distance)
			{
				indexOfDistant = index;
				distance = currentOffset - offset;
			}

			else if (offset > currentOffset + distance)
			{
				indexOfDistant = index;
				distance = offset - currentOffset;
			}
		}
		if (kInvalidIndex != indexOfDistant)
		{
			foundIndex = indexOfDistant;
		}
		else if (kInvalidIndex != indexOfLast)
		{
			foundIndex = indexOfLast;
		}
		else if (kInvalidIndex != indexOfFirst)
		{
			foundIndex = indexOfFirst;
		}
		else
		{
			assert(false);
			foundIndex = 0;
		}
		// take out from the old position in list...
		int prev = mRecentList[2 * foundIndex];
		int next = mRecentList[2 * foundIndex + 1];
		mRecentList[2 * prev + 1] = next;
		mRecentList[2 * next] = prev;
		// move to back, behind last...
		int last = mRecentList[2 * cacheSize];
		mRecentList[2 * last + 1] = foundIndex;
		mRecentList[2 * cacheSize] = foundIndex;
		mRecentList[2 * foundIndex] = last;
		mRecentList[2 * foundIndex + 1] = cacheSize;
	}
	assert(kInvalidIndex != foundIndex);

	if (foundIndex != kInvalidIndex)
	{
		mChunkOffsets[foundIndex] = offset;
		::memcpy(&mBuffer[foundIndex * mChunkSize], mAsyncBuffer, mChunkSize);
	}
}

UINT FileHandler::GetNextChunkIndex(UINT inChunk, bool inUpward) const
{
	UINT nextChunk = inChunk;
	if (inUpward)
	{
		if ((nextChunk + 1) * static_cast<LONGLONG>(mChunkSize) < mFileLength)
		{
			++nextChunk;
		}
	}
	else if (0 < nextChunk)
	{
		--nextChunk;
	}
	return nextChunk;
}

int FileHandler::ReadChunk(UINT inChunk)
{
	auto iterator = std::find(mChunkOffsets.begin(), mChunkOffsets.end(), inChunk);
	if (mChunkOffsets.end() != iterator)
	{
		return iterator - mChunkOffsets.begin();
	}

	DWORD actualReadBytes = 0;
	if (mIsReadingAsync)
	{
		BOOL success = ::GetOverlappedResult(mFile, mOverlappedPtr,
			&actualReadBytes, true);
		mIsReadingAsync = false;
		if (success)
		{
			AcceptAsyncData();
			iterator = std::find(mChunkOffsets.begin(), mChunkOffsets.end(), inChunk);
		}
	}

	if (mChunkOffsets.end() == iterator)
	{
		StartReadingChunk(inChunk);
		if (::GetOverlappedResult(mFile, mOverlappedPtr,
			&actualReadBytes, true))
		{
			AcceptAsyncData();
			iterator = std::find(mChunkOffsets.begin(), mChunkOffsets.end(), inChunk);
		}
		mIsReadingAsync = false;
	}

	return (mChunkOffsets.end() == iterator ? 
		kInvalidIndex : iterator - mChunkOffsets.begin());
}

const BYTE* FileHandler::GetChunkBufferForAddress(LONGLONG inAddress)
{
	const BYTE* result = nullptr;
	if (inAddress < mFileLength)
	{
		int index = ReadChunk(UINT(inAddress / mChunkSize));
		if (kInvalidIndex != index)
		{
			result = &mBuffer[index * mChunkSize];
		}
	}
	return result;
}

